import Core.WindFunctions as Wind
import Core.Gadget as Gadget
import Core.MongoDB as MongoDB
import Option.OptionGadget
import DataProcess as DataProcess
import datetime
import re

# 修正字段
def UpdateField(database, datebaseName, collectionName):

    docs = database.find(datebaseName, collectionName)
    for doc in docs:
        print(doc["Symbol"])

        if doc["Symbol"] == "600656.SH":
            kkwood = 1

        key = doc["Key"]
        update = {}

        properties = doc["Properties"]
        for name,value in properties.items():
            doc[name] = value

        values = doc["Values"]
        for name,value in values.items():
            doc[name] = value

        database.update(datebaseName,collectionName,{"Key":key},doc)
        kkwood = 1

    kkwood = 1


def UpdateField_Term(database):

    docs = database.Find("Instruments", "Option", filter={"Code": "510050.SH"})
    for doc in docs:
        print(doc["Symbol"])
        key = doc["Key"]

        #
        datetime2 = doc["DateTime2"]
        termNum = ((datetime2.year - 2000) * 100) + datetime2.month
        doc["Term"] = termNum

        #
        database.update("Instruments", "Option", {"Key": key}, doc)
        kkwood = 1

    kkwood = 1


# strikeMutiple: Strike 价格需要变成整数需要的乘数
def HumanReadSymbol(code, term , putcall, strike, strikeMutiple=1000):
    term = round(term)
    strike = round(strike * strikeMutiple)
    #
    humanSymbol = "-".join([code, str(term), putcall, str(strike)])
    return humanSymbol


# 还原复权前的单位和行权价格
def GetOriginalStrike(unit, strike, defaultUnit=10000):
    originalStrike = round(unit * strike * (1 / defaultUnit), 2)
    return originalStrike


def GenerateOptionDocument(data):
    doc = {}
    #
    doc["Underly"] = data["option_mark_code"]
    doc["Code"] = doc["Underly"]
    symbol = data["wind_code"] + ".SH"
    doc["Exchange_Code"] = "SH"
    doc["Symbol"] = symbol
    doc["Raw_Code"] = data["wind_code"]
    doc["Exchange"] = "SSE"
    doc["Instrument_Type"] = "Option"
    doc["Option_Type"] = data["option_type"]
    doc["Currency"] = "CNY"
    doc["Key"] = doc["Symbol"]

    # 510050C1503M02200
    tradeCode = data["trade_code"]
    doc["Trade_Code"] = tradeCode
    doc["Description"] = data["sec_name"]

    # ---SSE Option---
    pattern = "^([0-9]{6})([CP])([0-9]{4})([a-zA-Z])([0-9]{0,6})$"
    b = re.match(pattern, doc["Trade_Code"])
    doc["AdjFactor"] = tradeCode[b.regs[4][0]:b.regs[4][1]]

    if data["call_or_put"] == "认购":
        doc["PutCall"] = "Call"
    elif data["call_or_put"] == "认沽":
        doc["PutCall"] = "Put"

    sTerm = data["limit_month"]
    sTerm = sTerm.split("-")

    # 1903 vs 201903
    doc["Term"] = (int(sTerm[0]) - 2000) * 100 + int(sTerm[1])
    doc["Begin_Price"] = data["reference_price"]

    # doc["DateTime1"] = Gadget.ParseDateTime(sDateTime1, "%Y-%m-%d")
    doc["DateTime1"] = data["listed_date"]
    doc["DateTime2"] = data["expire_date"]
    doc["Expire_Date"] = doc["DateTime2"]
    doc["Date"] = doc["DateTime1"]
    doc["Settlement_Date"] = data["settlement_date"]
    doc["Status"] = data["contract_state"]

    #
    doc["Strike"] = data["exercise_price"]
    doc["Unit"] = data["contract_unit"]

    #
    if doc["AdjFactor"] != "M":
        doc["Adj_Strike"] = data["exercise_price"]
        doc["Adj_Unit"] = data["contract_unit"]
        # 还原 Strike and Unit
        originalStrike = GetOriginalStrike(data["contract_unit"], data["exercise_price"], defaultUnit=10000)
        doc["Strike"] = originalStrike
        doc["Unit"] = 10000

    #
    doc["Human_Symbol"] = HumanReadSymbol(doc["Code"], doc["Term"], doc["PutCall"], doc["Strike"], strikeMutiple=1000)

    #
    return doc


# 更新期权合约
def Download_Option_List(database, realtime=None, datetime2=None):
    #
    if datetime2 == None:
        datetime2 = datetime.datetime.now()

    Download_Option_List_by_Underly(database, "510050.SH", realtime=realtime, datetime1=datetime.datetime(2020, 1, 1),
                                    datetime2=datetime2)
    Download_Option_List_by_Underly(database, "510300.SH", realtime=realtime, datetime1=datetime.datetime(2020, 1, 1),
                                    datetime2=datetime2)


# 处理ETFOption复权问题
def ProcessETFOptionAdjustFactor(database,
                                 documentAdjusted, # 库中的全部不等于M期权
                                 documentTotal): # 库中全部期权
    #
    print("Process ETF Option Unit and Strike Adjustation")
    dfTotal = Gadget.DocumentsToDataFrame(documentTotal,
                                          keep=["symbol", "human_symbol", "date", "expire_date", "adjfactor"])

    # 循环被复权过的期权
    for document in documentAdjusted:
        symbol = document["symbol"]

        # 找到替代复权期权的“新正常期权”, 用来确认复权发生时间
        humanSymbol = document["human_symbol"]
        dfTmp = dfTotal[(dfTotal["human_symbol"] == humanSymbol) & (dfTotal["adjfactor"] == "M")]
        if dfTmp.empty:
            print(humanSymbol, "未能找到复权期权的原始期权 Can't Link AdjOption", symbol, "to Original")
            continue

        original = dfTmp.iloc[0]
        originalSymbol = original["symbol"]
        originalDate = original["date"]
        # print(humanSymbol, "Link AdjOption", symbol, "to Original", originalSymbol)

        # Generate Update Field
        # for A B C D
        tmpDocument = {}
        tmpDocument["adj_date"] = originalDate
        tmpDocument["adj_to_symbol"] = originalSymbol
        tmpDocument["adj_from_symbol"] = None
        database.Update("Instruments", "Option", {"Key": symbol}, tmpDocument)
        # for M 记住M永远是最新那个
        tmpDocument = {}
        tmpDocument["adj_to_symbol"] = None
        tmpDocument["adj_from_symbol"] = symbol
        database.Update("Instruments", "Option", {"Key": originalSymbol}, tmpDocument)

    pass

# ---更新期权合约列表---
# w.wset("optioncontractbasicinfo",
#           "startdate=2019-08-28;
#           enddate=2020-02-28;
#           exchange=sse;
#           windcode=510050.SH;
#           status=all") all / delisted / trading ( 全部 / 已退市 / 正在上市交易)
# Field 参数不必填写
# "field=wind_code,trade_code,sec_name,option_mark_code,option_type,call_or_put,exercise_mode,exercise_price,contract_unit,limit_month,listed_date,expire_date,exercise_date,settlement_date,reference_price,settle_mode,contract_state","cols=17;rows=130")
def Download_Option_List_by_Underly(database, underlySymbol, realtime=None, datetime1=None, datetime2=None):
    #
    if datetime1 == None:
        datetime1 = datetime(2015, 1,1)
    if datetime2 == None:
        datetime2 = datetime.now()

    # ---确定库中全部已存在期权，无论状态---
    # ---更新上市状态---
    nowDate = datetime2.date()
    instrumentInDatabseBySymbol = {}
    instrumentsInDatabase = database.Find("Instruments", "Option", filter={"Code": underlySymbol})
    for instrument in instrumentsInDatabase:
        symbol = instrument["symbol"]
        instrumentInDatabseBySymbol[symbol] = instrument
        #
        status = instrument["status"]
        expiredDate = instrument["datetime2"].date()
        #
        if status == "正在上市交易" and nowDate > expiredDate: # 库中该退市的退市
            tmpDocument = {}
            tmpDocument["Status"] = "退市"
            database.Update("Option", "Instruments", {"Key": symbol})
        else:
            pass

    for instrument in instrumentsInDatabase:
        symbol = instrument["symbol"]

    # ---确认数据库中最大日期---
    maxDate = database.ExecuteSQL("financial_data", "Select max(date) from financial_data.instrument_option where Code='" + underlySymbol + "'")
    maxDate = maxDate[0][0]
    print("Last Update by", maxDate)

    # ---读取全部期权---
    params = {}
    params["windcode"] = underlySymbol
    params["startdate"] = Gadget.ToDateString(datetime1)
    params["enddate"] = Gadget.ToDateString(datetime2)
    # params["startdate"] = "20191001"
    # params["enddate"] = "20191031"
    params["exchange"] = "sse" # "上交所" # 改参数不起作用
    params["status"] = "all" # "全部" # 该参数不起作用
    data = Wind.WSET("optioncontractbasicinfo", params)

    # ---Options in Wind---
    documentAdjusted = []
    instrumentWindBySymbol = {}
    for d in data:
        doc = GenerateOptionDocument(d)
        symbol = doc["Symbol"]
        instrumentWindBySymbol[symbol] = doc

        # 后续处理赋权问题
        if doc["AdjFactor"] != "M":
            documentAdjusted.append(doc)
            # 发生复权应该重新插入
            # database.Upsert("Instruments", "Option", {"Key": symbol}, doc)

        # Add new contract to Database
        if symbol not in instrumentInDatabseBySymbol.keys():
            print("New Contract", symbol, doc["Human_Symbol"], "DateTime1", doc["Date"])
            # 新期权插入
        database.Upsert("Instruments", "Option", {"Key": symbol}, doc)

    # ---处理复权带来的问题---
    documentTotal = database.Find("Instruments", "Option", filter={"Code": underlySymbol})
    datetimex = datetime.datetime(2015,1,1)
    documentAdjusted = database.Find("Instruments", "Option", filter=[("Code", underlySymbol),("AdjFactor","!=","M"),("Date", ">", datetimex)])
    ProcessETFOptionAdjustFactor(database, documentAdjusted, documentTotal)

    #
    documentListed = database.Find("Instruments", "Option", filter={"Code": underlySymbol, "Status": "上市"})
    print("Total Listed Options # " + str(len(documentListed)) + ", New Contract # " + str(len(instrumentWindBySymbol)))

    # if realtime != None:
    #     realtime.Set("OptionsInTrading", documentListed)

    print("Finish to Update Option Contract")


# ---无法通过API获得全部期权列表，所用先导入excel，再从excel导入database--
def Request_Write_Option_List_FromExcel(database, pathfilename, forcToUpdate=False):

    optionBySymbol = {}
    options = database.Find("Instruments", "Option")
    for option in options:
        symbol = option["Symbol"]
        optionBySymbol[symbol] = option

    header, table = IO.ReadExcelFile(pathfilename,"Sheet1")

    #Restructure header,由于Excel格式，第一次读出的header不正确，需要重构
    headerIndexByName = {}
    for i in range(0, len(table)):

        content = table[i]
        # header
        # Process Headers
        if i == 3:
            headerCount = 0
            for header in content:
                headerIndexByName[header] = headerCount
                headerCount = headerCount + 1
            continue

        # skip lines
        if i < 5:
            continue

        data = {}
        for key,index in headerIndexByName.items():
            data[key] = content[index]

        doc = GenerateOptionDocument(data)
        symbol = doc["Symbol"]
        print(symbol, i)

        update = False
        if forcToUpdate == False:
            if symbol not in optionBySymbol:
                update = True
            elif optionBySymbol[symbol]["Status"] != doc["Status"]:
                update = True
        else:
            update = True

        if update:
            print("Update Option", symbol)
            database.upsert("Instruments", "Option", {"Key": doc["Symbol"]}, doc)

    kkwood = 0


def Check_Missing_Bar(database):

    # Gadget.GetTradingDays_DataFrame(database, )
    num_missing = 0
    instruments = database.Find("Instruments", "Option")
    for instrument in instruments:
        symbol = instrument["symbol"]
        maxDate = database.ExecuteSQL("financial_data",
                                      "Select * from financial_data.option_dailybar where symbol='" + symbol + "' and open is Null")

        missing = len(maxDate)
        if missing > 0:
            num_missing += len(maxDate)
            print(symbol, "#", len(maxDate))

        # if missing > 3:
        #     real_datetime1 = instrument.get("datetime1")
        #     real_datetime2 = instrument.get("datetime2")
        #     DataProcess.Download_DailyBars(database, symbol, "option", real_datetime1, real_datetime2, None)

    print("Total Missing", num_missing)


def Fix_Missing_Bar(database):

    # Gadget.GetTradingDays_DataFrame(database, )
    num_missing = 0
    instruments = database.Find("Instruments", "Option")
    for instrument in instruments:
        real_datetime1 = instrument.get("datetime1")
        real_datetime2 = instrument.get("datetime2")
        symbol = instrument["symbol"]

        if real_datetime2 > datetime.datetime(2020,12,21):
            print(symbol)
            maxDate = database.ExecuteSQL("financial_data",
                                          "Delete from financial_data.option_dailybar where symbol='" + symbol + "' and date > 20201221")
            a = 0



    print("Total Missing", num_missing)


if __name__ == '__main__':
    #
    # ---Connect to DataBase, Find Series 连接数据库---
    from Core.Config import *
    pathfilename = os.getcwd() + "\..\Config\config2.json"
    config = Config(pathfilename)
    database = config.DataBase("JDMySQL")
    realtime = config.RealTime()

    # Fix_Missing_Bar(database)

    Wind.w.start()
    # CheckProgress()

    Check_Missing_Bar(database)


    datetime1 = datetime.datetime(2015, 4, 1)
    datetime2 = datetime.datetime(2015, 4, 20)
    DataProcess.Download_DailyBars(database, "10000019.SH", instrument_type="Option", begDateTime=datetime1, endDateTime=datetime2)
    DataProcess.Download_Daily_Quote(database, instrument_type="Option", datetime1=datetime1, datetime2=datetime2)

    # ---Update Option List---
    Download_Option_List(database=database, realtime=None)

    # ---冷啟動---
    DataProcess.Download_Daily_Quote(database, instrument_type="Option", start_index=1733)

    # ---Automatically download Data---
    ending_datetime = datetime.datetime(2020,12,31)
    DataProcess.Automatic_Download_DailyQuote(database, ending_datetime, instrumentType="Option")
